/* =========================================================
 * String prototype extends funciton
 * =========================================================
 *  依赖Jquery extend和utils工具函数
 *  method list: 
 *
 *                String.isUndefined
 *                String.isString
 *                String.isEmpty
 *                String.isBlank
 *                String.isNull
 *                String.isNullString
 * 
 *                insert
 *                append
 *                equal
 *                equalIgnoreCase
 *                reverse
 *                include
 *                startWith
 *                endWith
 *                empty
 *                blank
 *                truncate
 *                trim
 *                capitalize
 *                toArray
 *                replaces
 *                replacesRegion
 *                scan
 *                replaceCharAt
 *                stripTags
 *                escapeHTML
 *                unescapeHTML
 *                times
 *                camelize
 *                underscore
 *                dasherize
 *                inspect
 *                isJSON
 *                evalJSON
 * =========================================================
 * */

(function(window, undefined) {
    $.extend(String, {
        interpret: function(value) {
            return value == null ? '' : String(value);
        },
        isUndefined: function(pattern) {
            return $.type(pattern) === "undefined" || pattern === null;
        },
        isString: function(pattern) {
            return $.type(pattern) === "string";
        },
        isEmpty: function(pattern) {
            return String.isUndefined(pattern) || pattern.empty();
        },
        isBlank: function(pattern) {
            return String.isUndefined(pattern) || pattern.blank();
        },
        isNull: function(pattern) {
            return String.isUndefined(pattern) || pattern.empty() ||
                 pattern.blank() || String.isNullString(pattern);
        },
        isNullString: function(pattern) {
            return pattern.equalIgnoreCase("null");
        },
        escape: function(pattern) {
            return String(pattern).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
        },
        format: function (pattern) {
            if (arguments.length == 0) {
                return null;
            }
            var args = Array.prototype.slice.call(arguments, 1);
            return pattern.replace(/\{(\d+)\}/g, function (m, i) {
                return args[i];
            });
        },
        specialChar: {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '\\': '\\\\'
        },
        Template: {
            initialize : function(template, pattern) {
                this.template = template.toString();
                //this.pattern = pattern || this.pattern;
                this.pattern = pattern || /(^|.|\r|\n)(#\{(.*?)\})/;
            },
            evaluate: function(object) {
                //if (object && $.isFunction(object)) {
                //    object = $.extend({ }, object);
                //}
                return this.template.replaces(this.pattern, function(match) {
                    if (object == null) {
                        return (match[1] + '');
                    }
                    var before = match[1] || '';
                    if (before == '\\') {
                        return match[2];
                    }
                    var ctx = object, expr = match[3],
                        pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;

                    match = pattern.exec(expr);
                    if (match == null) {
                        return before;
                    }
                    while (match != null) {
                        var comp = match[1].startWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
                        ctx = ctx[comp];
                        if (null == ctx || '' == match[3]) break;
                        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
                        match = pattern.exec(expr);
                    }
                    return before.append(String.interpret(ctx));
                });
            }
        }
    });

    $.extend(String.prototype, (function() {

        /**
         * 
         * 插入字符串到指令位置之后
         * @param  {Number} index     索引
         * @return {String}
         * 1.如果只提供索引或者不提供任何参数返回字符串本身
         * 2.如果index不为undefined, str为undefined，等同直接调用append(index)
         * 2.如果index为String, str为String,返回valueOf
         * example:
         *     1."javascript".insert(6) or "javascript".insert() 
         *      --> "javascript6"
         * 
         *     2."javascript".insert("1.6")
         *      --> "javascript1.6"
         *
         *     3."javascript".insert("1", "1.6")
         *      --> "javascript"
         */
        insert = function (index, str) {
            str = str || "", type = $.type(index);

            if(str.empty()) {
                return this.append(index);
            }
            //if(!$.isNumeric(index)) // $.isNumeric("3") is true
            if($.type(index) !== "number") {
                return this.valueOf();
            }
            if (index < 0 || index >= this.length - 1) {
                return this.append(str);
            }
            return this.substring(0, index + 1).append(str).append(this.substring(index + 1));
        }

        /**
         * 追加字符串，append可以视为concat函数的别名
         * 修正当concat提供undefined参数时追加"undefined"的问题
         * @param  {String} str   追加字符串
         * @return {String}
         */
        append = function (str) {
            str = str || "";
            return this.concat(str);
        }

        /**
         * 替换指定索引处的字符
         * @param  {Number} index    索引
         * @param  {String} ch       替换字符
         * @return {String}
         * @Deprecated
         */
        replaceCharAt = function (index, ch) {
            //if(index === undefined || ch === undefined) {
            if(String.isUndefined(index) || String.isUndefined(ch)) {
                return this.valueOf();
            }
            var len = this.length;
            if(index < 0 || index > len - 1) {
                console.error("the index [".append(index).append("] is out of string lenght range."))
                return this.valueOf();
            } else {
                //this_arr = this.toArray();
                //this_arr[index] = ch;
                //return this_arr.join("");
                return this.substring(0, index).append(ch).append(this.substring(index + 1));
            }
        }

        /**
         * 删除索引处字符，索引越界时候返回原字符串
         * @Deprecated
         */
        deleteCharAt = function (index) {
            var len = this.length;
            if (index === undefined || index < 0 || index >= len) {
                console.error("the index [".append(index).append("] is out of string lenght range."))
                return this.valueOf();
            } else if (index == 0) {
                return this.substr(1, len - 1);
            } else if (index == len - 1) {
                return this.charAt(len - 1);
            } else {
                //this.toArray().splice(0, index).append(this.toArray().splice(index + 1));
                return this.substring(0, index).append(this.substring(index + 1));
            }
        }

        /**
         * 删除指定索引区间的子串，如果开始索引大于介绍索引，返回原字符串
         * 删除原则左闭右开，如果不提供end索引则调用substr()进行截断
         * @param start  {Number}   开始索引
         * @param end    {Number}   结束索引
         * @Deprecated
         */
        deleteSubStr = function (start, end) {
            if(!$.isNumeric(start)) {
                return this.valueOf();
            }
            var len = this.length;
            end = (end === undefined ? len : end);
            if (start == end) {
                return this.deleteCharAt(start);
            } else {
                start = (start < 0 ? 0 : start);
                end = (end > len - 1 ? len - 1 : end);
                if (start > end) {
                    console.error("the start [".append(start).append("] index is less the end [").append(end).append("] index"));
                    return this.valueOf();
                }
                return this.substring(0, start).append(this.substring(end + 1 , this.length));
            }
        }

        /**
         * 是否包含指定的字符串
         * @param  {String} str       子字符串
         * @return {Boolean}          [true：包含，false：不包含]
         */
        include = function(str) {
            return this.indexOf(str) > -1;
        }

        /**
         * 检查字符串是否以给定字符串开头, 也可以在指定位置之后进行查找
         * @param  {String} pattern         查找内容或模式
         * @param  {Number} index           开查找位置,如果不提供从索引0开始匹配
         * @return {Boolean}                [true：包含，false：不包含]
         */
        startWith = function (pattern, index) {
            index = $.isNumeric(index) ? index : 0;
            return this.lastIndexOf(pattern, index) === index;
        }

        /**
         * 检查字符串是否以给定字符串结尾, 也可以在指定位置之后进行查找
         * @param  {String} pattern         查找内容或模式
         * @param  {Number} index           开查找位置,如果不提供从索引最后开始匹配
         * @return {Boolean}                [true：包含，false：不包含]
         */    
        endWith = function (pattern, index) {
            pattern = String(pattern);
            index = $.isNumeric(index) ? index : this.length;
            if (index < 0) {
                index = 0;
            }
            if (index > this.length) {
                index = this.length;
            }
            var position = index - pattern.length;
            return position >= 0 && this.indexOf(pattern, position) === position;
        }

        /**
         * 空字符串'',""判断
         * @return {Bool}         [true：为空，false：不为空]
         */
        empty = function () {
            return this == '';
        }

        /**
         * 非打印字符/空白字符判断
         * @return {Boolean}         [true：是，false：否]
         */    
        blank = function () {
            return /^\s*$/.test(this);
        }

        // move to -> String.isUndefined(str)
        //function isUndefined() {
        //    return $.type(this) === "undefined";
        //}

        /**
         * 指定字符串和当前字符串是否相等，包括类型 "12" ≠ 12
         * @param  {String} pattern        需要比较的字符串
         * @return {Boolean}               [true：相等，false：不相等]
         */
        equal = function (pattern) {
            var len = this.length;
            if (String.isUndefined(pattern) || !String.isString(pattern) || len != pattern.length) {
                return false;
            } else {
                return this == pattern;
            }
            //return this == pattern;
            //return this === pattern;
        }

        /**
         * 指定字符串和当前字符串是否相等,不区分大小写,区分类型
         * @see {String#equal}
         * @param  {String} pattern     需要比较的字符串
         * @return {Bool}               [true：相等，false：不相等]
         */
        equalIgnoreCase = function (pattern) {
            return this.toLowerCase().equal(pattern.toLowerCase());
        }

        /**
         * 将字符串反序排列
         * @return {String}
         */
        reverse = function () {
            //return this.split("").reverse().join("");
            return this.toArray().reverse().join("");
        }

        /**
         * 拆分字符串为数组
         * @return {Array}
         */
        toArray = function () {
            return this.split("");
        }

        /**
         * 过滤前后空格
         * @return {String}
         */
        trim = function () {
            return this.replace(/(^\s*)|(\s*$)/g, "");
        }

        /**
         * 首字母大写
         * @return {String} 
         */
        capitalize = function () {
            return this.charAt(0).toUpperCase().append(this.substring(1).toLowerCase());
        }

        /**
         * 保留指定长度的字符,并追加默认显示后缀
         * @param  {Number} length        保留字符总长度
         * @param  {String} truncation    显示后缀,默认为"..."
         * @return {String} 
         */
        truncate = function (length, truncation) {
            length = length || 30;
            truncation = String.isUndefined(truncation) ? '...' : truncation;
                return this.length > length ?
                    this.slice(0, length - truncation.length).append(truncation) : String(this);
        }

        /**
         * 清理HTML标签
         * @return {String}
         */
        stripTags = function () {
            return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?(\/)?>|<\/\w+>/gi, '');
        }

        /**
         * 转换HTML特殊字符为实体字符
         * @return {String}
         */
        escapeHTML = function () {
            return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
        }

        /**
         * 转换实体字符HTML特殊字符
         * @return {String}
         */
        unescapeHTML = function () {
            return this.replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
        }

        /**
         * 初始化模板,私有方法
         * @param  {Function | String} replacement 模板函数或模板正则
         * @return {Function | String}
         */
        prepareReplacement = function (replacement) {
            if ($.isFunction(replacement)) {
                return replacement;
            }
            //var template = new Template(replacement);
            String.Template.initialize(replacement);
            return function(match) {
               //return template.evaluate(match);
               return String.Template.evaluate(match);
            };
        }

        // In some versions of Chrome, an empty RegExp has "(?:)" as a `source`
        // property instead of an empty string.
        isNonEmptyRegExp = function (regexp) {
            return regexp.source && regexp.source !== '(?:)';
        }

        /**
         * 根据匹配字符或函数对字符串进行替换
         * @param  {String} pattern                  匹配正则,正则表达式中不支持/g参数
         * @param  {String | Function} replacement   匹配字符串或者匹配函数
         * @return {String}
         * 1.replacement 如果为String 等价于replace方法
         * 2.replacement 如果为Function 可以使用方法对返回匹配内容进行替换
         * example：
         *      var events = 'click keyup select moveover';
         *      events.replaces(/\w+/, function(match) { 
         *          return 'on' + match[0].capitalize()
         *      });
         *      output--> `onClick onKeyup onSelect onMoveover`
         */
        replaces = function (pattern, replacement) {
            var result = '', source = this, match;
            replacement = prepareReplacement(replacement);
            //if(Object.prototype.toString.call(pattern) === "String")
            if(String.isString(pattern)) {
                pattern = String.escape(pattern);
            }

            if (!(pattern.length || isNonEmptyRegExp(pattern))) {
                replacement = replacement('');
                return replacement.append(source.split('').join(replacement)).append(replacement);
            }

            while (source.length > 0) {
                match = source.match(pattern)
                if (match && match[0].length > 0) {
                    result += source.slice(0, match.index);
                    result += String.interpret(replacement(match));
                    source  = source.slice(match.index + match[0].length);
              } else {
                    result += source, source = '';
              }
            }
            return result;
        }

        /**
         * 根据匹配字符或函数和匹配数量对字符串进行部分替换
         * @param  {String} pattern                  匹配正则,正则表达式中不支持/g参数
         * @param  {String | Function} replacement   匹配字符串或者匹配函数
         * @param  {Number} count                    匹配数量,默认只匹配第一个匹配项
         * @return {String}
         */
        replacesRegion = function (pattern, replacement, count) {
            replacement = prepareReplacement(replacement);
            count = String.isUndefined(count) ? 1 : count;

            return this.replaces(pattern, function(match) {
                if (--count < 0) return match[0];
                return replacement(match);
            });
        }

        /**
         * 根据匹配模式迭代处理每个匹配事件
         * @param  {String} pattern           正则表达式
         * @param  {Function} iterator        迭代函数
         * @return {String}
         * example：
         *       1.'js, javascript & java'.scan(/\w+/, alert);
         *       output-->'js', 'javascript' and 'java'分别执行alert函数调用
         *       2.'success-message & error-message '.scan(/(\w|-)+/, $('dom').toggle);
         *       在匹配的DOM元素执行相应的效果切换
         */
        scan = function (pattern, iterator) {
            this.replaces(pattern, iterator);
            return String(this);
        }

        /**
         * 字符串重复
         * @param  {Number} count           要重复的次数
         * @return {String}
         */
        times = function (count) {
            if(String.isUndefined(count)) {
                count = -1;
            }
            return count < 1 ? '' : new Array(count + 1).join(this);
        }

        /**
         * 包含'-'的字符串转换为驼峰规则字符串
         * @return {String}
         */
        camelize = function () {
            return this.replace(/-+(.)?/g, function(match, char) {
                return char ? char.toUpperCase() : '';
            });
        }

        /**
         * 驼峰规则字符串或包含'-'字符串使用下划线连接
         * @return {String}
         */
        underscore = function () {
            return this.replace(/::/g, '/')
                   .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
                   .replace(/([a-z\d])([A-Z])/g, '$1_$2')
                   .replace(/-/g, '_')
                   .toLowerCase();
        }

        /**
         * 替换所有的下划'_'为连接线'-'
         * @return {String}
         */
        dasherize = function () {
            return this.replace(/_/g, '-');
        }

        /**
         * 验证字符串是否是合法的json字符串
         * @return {Boolean}
         */
        isJSON = function () {
            var str = this;
            if (str.blank()) return false;
            str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
            str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
            str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
            return (/^[\],:{}\s]*$/).test(str);
        }

        /**
         * 执行json字符串对象
         * @return {JSON}
         */
        evalJSON = function () {
            var json = this.unfilterJSON();
            return JSON.parse(json);
        }

        /**
         * 转义字符串查看
         * @param  {Boolean} useDoubleQuotes     [TRUE | FALSE]
         * @return {String}
         */
        inspect = function (useDoubleQuotes) {
            var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
                if (character in String.specialChar) {
                    return String.specialChar[character];
                }
                return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
            });
            if (useDoubleQuotes) {
                return '"' + escapedString.replace(/"/g, '\\"') + '"';
            }
            return "'" + escapedString.replace(/'/g, '\\\'') + "'";
        }

        /**
         * json过滤规则
         * @param  {String} filter          过滤规则
         * @return {String}
         */
        unfilterJSON = function (filter) {
            return this.replace(filter || /^\/\*-secure-([\s\S]*)\*\/\s*$/, '$1');
        }

        // TODO
        interpolate = function (object, pattern) {
            return String.Template.initialize(this, pattern).evaluate(object);
        }

        /**
         * 如果ES标准实现相应的函数则使用标准实现，否则使用自定义实现
         * 如果为私有函数不提供返回句柄
         */
        return {
            insert:           String.prototype.insert || insert,
            append:           String.prototype.append || append,
            equal:            String.prototype.equal || equal,
            equalIgnoreCase:  String.prototype.equalIgnoreCase || equalIgnoreCase,
            reverse:          String.prototype.reverse || reverse,
            include:          String.prototype.include || include,
            startWith:        String.prototype.startsWith || startWith,
            endWith:          String.prototype.endsWith || endWith,
            empty:            String.prototype.empty || empty,
            blank:            String.prototype.blank || blank,
            truncate:         String.prototype.truncate || truncate,
            trim:             String.prototype.trim || trim,
            capitalize:       String.prototype.capitalize || capitalize,
            toArray:          String.prototype.toArray || toArray,
            replaces:         String.prototype.replaces || replaces,
            replacesRegion:   String.prototype.replacesRegion || replacesRegion,
            scan:             String.prototype.scan || scan,
            replaceCharAt:    String.prototype.replaceCharAt || replaceCharAt,
            stripTags:        String.prototype.stripTags || stripTags,
            escapeHTML:       String.prototype.escapeHTML || escapeHTML,
            unescapeHTML:     String.prototype.unescapeHTML || unescapeHTML,
            times:            String.prototype.times || times,
            camelize:         String.prototype.camelize || camelize,
            underscore:       String.prototype.underscore || underscore,
            dasherize:        String.prototype.dasherize || dasherize,
            inspect:          String.prototype.inspect || inspect,
            isJSON:           String.prototype.isJSON || isJSON,
            evalJSON:         String.prototype.evalJSON || evalJSON
          };
    })());
})(window);